function MatchAtoms(item, task) {
  this.parent = item;
  this.type = "match_atoms";
  if (task.options && task.options.atoms && task.options.labels) {
    this.options = task.options;
  } else {
    this.options = { atoms : ["H-1", "H-2", "He-3", "He-4"], labels : ["mass", "neutrons"] }
  }
  this.drops = shuffle(this.options.atoms);
  this.attempts = 0;
}

var mouse = { down : false, z : 16 };

MatchAtoms.prototype = {
  edit : function() {
    this.show($("#item .content"));
    this.showAtomOptions();
    this.showLabelOptions();
  },

  show : function(target) {
    Task.prototype.show.apply(this, arguments);
    this.positions = ["", "", "", ""];

    this.showAtoms();
    this.showDrops();
    this.easel.append("<div class='p-absolute' style='right: -8px; bottom: -8px;'>" +
      "<span>When finished click the check mark to check your answer&nbsp;&nbsp;&rarr;</span>&nbsp;&nbsp;" +
      "<a class='score-button' enabled='false' onclick='lesson.items.current.task.score(false);'>" +
      "<img src='/images/apply_gray.png' style='opacity: 0.5; vertical-align: middle;'/>" +
      "</a></div>");
    this.initializeMouseHandlers();
  },

  showAtoms : function() {
    for (var i = 0; i < this.options.atoms.length; i++) {
      var a = i2a(this.options.atoms[i]);
      var e = this.easel;
      var id  = "atom-" + i;
      e.append("<canvas width='220px' height='220px' class='atom' id='" + id + "'></canvas>");
      e.find("#" + id).css({ position : "absolute", left : 12 + 0.25 * i * this.easel.width(), top : -20 });
      var ctx = e.find("#" + id)[0].getContext("2d");
      var x = 0.5 * ctx.canvas.width;
      var y = 0.5 * ctx.canvas.height;
      var scale = a.name == "Hydrogen" || a.name == "Helium" ? 96 : 72;
      a.drawAtomicModel(ctx, x, y, scale);
    }
    this.easel.atoms = e.find(".atom");
  },

  showDrops : function() {
    var e = this.easel;
    var target_size = 180;
    for (var i = 0; i < this.drops.length; i++) {
      var a = i2a(this.drops[i]);
      var id = "drop-" + i;
      e.append("<div class='drop a-center' id='" + id + "'></div>");
      var drop = e.find("#" + id);
      drop.size = 208;
      drop.css({ position : "absolute", left : 16 + 0.25 * i * this.easel.width(), top : 200, width : drop.size });
      drop.html("<div class='target border-2'><div class='center'><span class='xxlarge'>?</span></div></div>" +
        "<div class='labels' style='padding: 8px 0;'></div>");
      drop.target = drop.find(".target");
      drop.target.center = drop.target.find(".center");
      drop.target.css({ width : target_size, height : target_size,
        margin : "0 " + (drop.size - target_size) / 2 + "px" });
      drop.labels = drop.find(".labels");
      for (var k = 0; k < this.options.labels.length; k++) {
        var c = this.options.labels[k];
        var text = c == "protons" ? "# of protons" : c == "neutrons" ? "# of neutrons" :
          c == "mass" ? "Atomic mass" : c == "number" ? "Atomic number" : "Unrecognized label format";
        drop.labels.append(
          text + " : " + (c == "number" ? a["protons"] : a[c]) + (k + 1 != this.options.labels.length ? "<br />" : ""));
      }
    }
  },

  initializeMouseHandlers : function() {
    var data = { task : this };
    var atoms = this.easel.find(".atom");
    atoms.mousedown(data, function(event) { event.data.task.atomMouseDown($(this), event) });
    atoms.mouseout(data, function(event) { event.data.task.atomMouseOut($(this), event) });
    atoms.mouseup(data, function(event) { event.data.task.atomMouseUp($(this), event) });
  },

  showAtomOptions : function() {
    Task.prototype.showOptions.apply(this, arguments);
    var options = ($("#item .options"));
    if (options.find(".atoms").length == 0) {
      options.append("<div class='atoms'></div>");
    }
    var atoms = options.find(".atoms");
    atoms.html("Atoms : ");
    for (var i = 0; i < 4; i++) {
      atoms.append("<select class='atom-" + i + "'></select>");
      var select = atoms.find("select.atom-" + i);
      for (var j in i2n) {
        select.append("<option value='" + j + "'" +
          (j == this.options.atoms[i] ? " selected='selected'" : "") + ">" +
          i2n[j] + "</option>");
      }
      select.change(function() {
        var item = lesson.items.current;
        var cn = this.className;
        var ix = cn.substring(cn.length - 1);
        item.task.options.atoms[ix] = this.value;
        item.task.drops = shuffle(item.task.options.atoms);
        var task = { type : item.task.type, options : item.task.options };
        item.saveProperty("task", JSON.stringify(task));
        item.edit();
      });
    }
  },

  showLabelOptions : function() {
    if ($("#item .options .labels").length == 0) {
      $("#item .options").append("<div class='labels' style='margin-top:8px;'></div>");
    }
    var labels = $("#item .options .labels").html("<hr />Labels : ");
    for (var j = 0 ; j < 4; j++) {
      var value = j == 0 ? "protons"  : j == 1 ?  "neutrons"  :  j  ==  2  ?  "number"  :  "mass";
      var text = j == 0 ? "# of protons" : j == 1 ? "# of neutrons" : j == 2 ? "atomic number" : "atomic mass";
      labels.append("<input type='checkbox' value='" + value + "' /><span>" + text + "</span>");
    }
    labels.inputs = labels.find("input");
    labels.inputs.css( { "zoom" : "120%", "position" : "relative", "bottom" : "1px", "margin" : "0 4px" } );
    labels.find("span").css( { "position" : "relative", "top" : "1px", "padding" : "0 48px 0 8px" } );
    for (var i = 0; i < labels.inputs.length; i++) {
      var input = $(labels.inputs[i]);
      if ($.inArray(input.val(), lesson.items.current.task.options.labels) > -1) {
        input.attr("checked", true);
      }
    }
    labels.inputs.change(function() {
      var item = lesson.items.current;
      var ix = $.inArray(this.value, item.task.options.labels);
      if ($(this).prop("checked") && ix < 0) {
        item.task.options.labels.push(this.value);
      } else if (!$(this).prop("checked") && ix >= 0) {
        var labels = item.task.options.labels;
        item.task.options.labels = [];
        for (var i = 0; i < labels.length; i++) {
          if (labels[i] != this.value) { item.task.options.labels.push(labels[i]); }
        }
      }
      var task = { type : item.task.type, options : item.task.options };
      item.saveProperty("task", JSON.stringify(task));
      item.edit();
    })
  },

  positionAtom : function(atom) {
    var e = this.easel;
    var drops = e.find(".drop");
    atom.x = atom.position().left + 0.5 * atom.width();
    atom.y = atom.position().top + 0.5 * atom.height();
    atom.id = atom.attr("id");
    atom.i = atom.id.substring(atom.id.length - 1);
    atom.name = this.options.atoms[atom.i];
    atom.position = $.inArray(atom.name, this.positions);
    if (atom.position > -1) {
      this.positions[atom.position] = "";
    }

    var dropped = false;
    for (var i = 0; i < drops.length; i++) {
      var drop = $(drops[i]);
      drop.x = drop.position().left + 0.5 * drop.width();
      drop.y = drop.position().top + 0.5 * drop.find(".target").height();
      var dx = atom.x - drop.x;
      var dy = atom.y - drop.y;
      var r = Math.pow(dx * dx + dy * dy, 0.5);
      
      if (r < 80 && this.positions[i] == "") {
        dropped = true;
        var left = parseInt(drop.x - 0.5 * atom.width());
        var top = parseInt(drop.y - 0.5 * atom.height() + 14);
        var duration = 32 * Math.pow(r, 0.5);
        atom.animate( { left : left, top : top }, duration);
        this.positions[i] = atom.name;
        break;
      }
    }
    for (var j = 0; j < drops.length; j++) {
      var target = $(drops[j]).find(".target");
      target.animate({ opacity : this.positions[j] != "" ? 0 : 1 }, 256);
    }
    if (!dropped) {
      atom.animate( { left : 12 + atom.i * 230, top : -20 }, 512);
    }
    if ($.inArray("", this.positions) < 0) {
      Task.prototype.enableScoreButton.apply(this, arguments);
    } else {
      Task.prototype.disableScoreButton.apply(this, arguments);
    }
  },

  score : function(edit) {
    if (this.easel.find(".score").length == 0) {
      this.easel.append("<div class='score p-absolute border-1 a-left'></div>");
    }
    this.easel.score = this.easel.find(".score").show();

    var complete = true;
    var correct = true;
    for (var i = 0; i < this.drops.length; i++) {
      if (this.positions[i] == "") {
        complete = false;
        correct = false;
        break;
      }
      if (this.drops[i] != this.positions[i]) {
        correct = false;
      }
    }
    if (complete) {
      this.attempts++;
    } else {
      this.easel.score.html("<p>Drag and drop each atom to the correctly labeled space. Click the " +
        "Score button again when you've completed this task.</p><p class='a-right'>" +
        "<button onclick='lesson.items.current.task.target.find(\".match_atoms .score\").hide()'>" +
        "Close</button></p>");
    }
    var score = { attempts : this.attempts, correct : correct };
    this.showScore(score);
    if (score.correct) {
      this.complete = true;
      this.recordScore(score);
      lesson.showNavigation();
    }
  },

  showScore : function(score) {
    if (score.correct) {
      this.complete = true;
      lesson.showNavigation();
      var onclick = "lesson.showNextSlide()";
      this.easel.score.html("<p style='margin-right:64px;'>You've successfully matched all the atoms to " +
        "the correct property value" + (this.options.labels.length > 1 ? "s" : "") + ". Good job.</p>" +
        "<a class='next p-absolute' style='right:16px;bottom:8px;' " +
        "onclick='" + onclick + "'><img src='/images/forward.png'></a>");
    } else {
      this.easel.score.html("<p style='margin-right: 64px;'>Atomic mass is proportional to " +
        "the sum of protons and neutrons in an atom.</p>" +
        "<a class='retry p-absolute' style='right:16px;bottom:8px;' " +
        "onclick='lesson.items.current.task.show()'><img src='/images/reload.png'></a>");
    }
  },

  recordScore : function(score) {
    Task.prototype.recordScore.apply(this, arguments);
  },

  atomMouseDown : function(atom, event) {
    lesson.disableSelection();
    if ($.inArray(atom[0], event.data.task.easel.atoms) > -1) {
      mouse.down = true;
      mouse.downEvent = event;
      mouse.target = $(event.currentTarget);
      mouse.target.left = mouse.target.position().left;
      mouse.target.top = mouse.target.position().top;
      mouse.target.css( { "z-index" : mouse.z++ } );
    }
  },

  atomMouseUp : function(atom, event) {
    mouse.down = false;
    if (atom.length > 0 && $.inArray(atom[0], event.data.task.easel.atoms) > -1) {
      lesson.items.current.task.positionAtom(atom);
    }
  },

  atomMouseOut : function(atom, event) {
  //  alert("mouse out!");
  },

  mouseMove : function(event) {
    if (mouse.down && mouse.target) {
      console.log("mouse move");
      var lesson = $("#lesson");
      var zoom = lesson.length != 0 ? lesson.css("zoom") : 1;
      var left = Number(mouse.target.left) + (event.clientX - mouse.downEvent.clientX) / zoom;
      var top = Number(mouse.target.top) + (event.clientY - mouse.downEvent.clientY) / zoom;
      if (0 < left && left < this.easel.width() && -20 < top && top < this.easel.height()) {
        mouse.target.css({
          left : left + "px",
          top : top + "px"
        }, 0);
      } else {
        this.atomMouseUp($(), event);
      }
  //        alert("mousedrag!");
    }
  }
};

function shuffle(array) {
  var a = array.slice();
  for (var i = 0; i < a.length; i++) {
    var ix = parseInt(Math.random() * a.length);
    var t = a[ix];
    a[ix] = a[i];
    a[i] = t;
  }
  return a;
}